JAVA笔记14__多线程共享数据(同步)/ 线程死锁 / 生产者与消费者应用案例 / 线程池

/**
* 多线程共享数据
* 线程同步:多个线程在同一个时间段只能有一个线程执行其指定代码,其他线程要等待此线程完成之后才可以继续执行。
* 多线程共享数据的安全问题,使用同步解决。
* 线程同步两种方法:
* 1.同步代码块
* synchronized(要同步的对象){ 要同步的操作 }
* 2.同步方法
* public synchronized void method(){ 要同步的操作 }
*/
public class Main {
public static void main(String[] args) {
MyThread s0 = new MyThread();
Thread t1 = new Thread(s0,"one");
Thread t2 = new Thread(s0,"two");
t1.start();
t2.start();
}
} class MyThread implements Runnable{
Object obj = new Object(); //同步的标记对象
@Override
public void run() {
//同步代码块
synchronized(obj){
System.out.println(Thread.currentThread().getName()+" is doing...");
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" finished.");
}
}
}
public class Main {
public static void main(String[] args) {
MyThread s0 = new MyThread();
Thread t1 = new Thread(s0,"one");
Thread t2 = new Thread(s0,"two");
t1.start();
t2.start();
}
} class MyThread implements Runnable{
@Override
public void run() {
doMethod();
}
/**
* 同步方法,同步的是当前对象(this)
*/
public synchronized void doMethod(){
System.out.println(Thread.currentThread().getName()+" is doing...");
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" finished.");
}
} /**
* 同步代码会带来性能降低的问题,担高数据的安全性
* 当编写synchronized块时,有几个简单准则可以遵循。
* 1.使代码块保持简短,把不随线程变化的预处理和后处理移出synchronized块。
* 2.不要阻塞。如InputStream.read()。
* 3.在持有锁的时候,不要对其它对象调用方法。
*/
/**
* 线程死锁:同步过多,会造成死锁
*/
public class Main {
public static void main(String[] args) {
new MyThread();
}
} class MyThread implements Runnable{
Customer c = new Customer();
Waiter w = new Waiter();
public MyThread(){
new Thread(this).start();
w.say(c);
}
@Override
public void run() {
c.say(w);
}
} class Customer{ //顾客
public synchronized void say(Waiter w){
System.out.println("顾客说:先做再给钱!");
w.doService();
}
public synchronized void doService(){
System.out.println("顾客同意了,先给钱再做");
}
}
class Waiter{ //服务员
public synchronized void say(Customer c){
System.out.println("服务员说:先给钱再做!");
c.doService();
}
public synchronized void doService(){
System.out.println("服务员同意了,先做再给钱");
}
}
//Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
//1.当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
//2.然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
//3.尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
//4.第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
//5.以上规则对其它对象锁同样适用。
/**
* 生产者与消费者应用案例:生产者不断生产产吕,消费者不断取走产品。
* sleep与wait的区别:sleep不释放对象锁,wait释放对象锁。
*/
public class Main {
public static void main(String[] args) {
Food f = new Food();
Productor p = new Productor(f);
Consumer c = new Consumer(f);
new Thread(p).start();
new Thread(c).start();
}
} class Productor implements Runnable{ //生产者:厨师
Food food;
public Productor(Food f){
this.food = f;
}
@Override
public void run() {
for(int i=0;i<100;++i){
if(i%2==0){
food.set("西红柿炒蛋"+i, "补充维生素C。"+i);
}else{
food.set("胡萝卜炒肉"+i, "补充维生素A。"+i);
}
}
}
}
class Consumer implements Runnable{ //消费者:服务员
Food food;
Consumer(Food f){
this.food = f;
}
@Override
public void run() {
for(int i=0;i<100;++i){
food.get();
}
}
}
class Food{
String name;
String content;
boolean flag = true; //true可以生产,false可以拿走
public synchronized void set(String name,String content){ //生产产品
if(!flag){
try {
this.wait(); //让当前线程进入等待池等待,没有指定时间,需要其它线程唤醒
//释放对象锁,让出CPU
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
this.name = name;
try {
Thread.sleep(300);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
this.content = content;
flag = false;
this.notify(); //唤醒在该监视器上的一个线程
}
public synchronized void get(){ //消费产品
if(flag){
try {
this.wait(); //让当前线程进入等待池等待,没有指定时间,需要其它线程唤醒
//释放对象锁,让出CPU
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
try {
Thread.sleep(300);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println(this.name+":"+this.content);
flag = true;
this.notify(); //唤醒在该监视器上的一个线程
}
}
/**
* 线程池是预先创建线程的一种技术。线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中,
* 然后对这些资源进行复用。减少频繁的创建和销毁对象。
*/
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread(); //创建一个单线程的线程池
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(t1);
es.execute(t2); //创建一个固定大小的线程池
ExecutorService es2 = Executors.newFixedThreadPool(2);
es2.execute(t1);
es2.execute(t2);
es2.execute(t3); //创建一个可缓存的线程池
ExecutorService es3 = Executors.newCachedThreadPool();
es3.execute(t1);
es3.execute(t2); //创建一个大小无限的线程池
ExecutorService es4 = Executors.newScheduledThreadPool(2);
es3.execute(t1);
es3.execute(t2);
}
} class MyThread implements Runnable{
@Override
public void run() {
for(int i=0;i<10;++i){
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("MyThread-"+i);
}
}
}
上一篇:Delphi另一个多线程函数:BeginThread用法


下一篇:[LeetCode] Binary Tree Preorder Traversal 二叉树的先序遍历