1.什么情况下需要同步 当多线程并发执行同一代码时 希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
2.同步代码块 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
public class TestMain {
public static void print1() {
//锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
synchronized(String.class){
System.out.print("1");
System.out.print("2");
}
}
//非静态同步函数的锁是:this (非静态方法已创建实例化对象后调用的)
public synchronized void print2() {
System.out.print("1");
System.out.print("2");
}
// 静态的同步函数的锁是:字节码对象.class (静态是初始化时形成的所以 是字节码对象)
public static synchronized void print3() {
System.out.print("1");
System.out.print("2");
}
}
多线程(两个线程间的通信)
- 1.什么时候需要通信 * 多个线程并发执行时, 在默认情况下CPU是随机切换线程的 * 如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
- 2.怎么通信 * 如果希望线程等待, 就调用wait() * 如果希望唤醒等待的线程, 就调用notify(); * 这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
public class Demo1_Notify {
/**
* @param args
* 等待唤醒机制
*/
public static void main(String[] args) {
final Printer p = new Printer();
new Thread() {
public void run() {
while(true) {
try {
p.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
p.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
//等待唤醒机制
class Printer {
private int flag = 1;
public void print1() throws InterruptedException {
synchronized(this) {
if(flag != 1) {
this.wait(); //当前线程等待
}
System.out.print("方法一");
System.out.print("\r\n");
flag = 2;
this.notify(); //随机唤醒单个等待的线程
}
}
public void print2() throws InterruptedException {
synchronized(this) {
if(flag != 2) {
this.wait();
}
System.out.print("方法2");
System.out.print("\r\n");
flag = 1;
this.notify();
}
}
}
多线程(三个或三个以上间的线程通信)
多个线程通信的问题 * notify()方法是随机唤醒一个线程 * notifyAll()方法是唤醒所有线程 * JDK5之前无法唤醒指定的一个线程 * 如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件
public class Demo2_NotifyAll {
/**
* @param args
*/
public static void main(String[] args) {
final Printer2 p = new Printer2();
new Thread() {
public void run() {
while(true) {
try {
p.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
p.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
p.print3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
1,在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
2,为什么wait方法和notify方法定义在Object这类中?
因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法和notify方法需要定义在Object这个类中
3,sleep方法和wait方法的区别?
a,sleep方法必须传入参数,参数就是时间,时间到了自动醒来wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待
b,sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡wait方法在同步函数或者同步代码块中,释放锁
class Printer2 {
private int flag = 1;
public void print1() throws InterruptedException {
synchronized(this) {
while(flag != 1) {
this.wait(); //当前线程等待
}
System.out.print("方法一");
System.out.print("\r\n");
flag = 2;
//this.notify(); //随机唤醒单个等待的线程
this.notifyAll();
}
}
public void print2() throws InterruptedException {
synchronized(this) {
while(flag != 2) {
this.wait(); //线程2在此等待
}
System.out.print("方法2");
System.out.print("\r\n");
flag = 3;
//this.notify();
this.notifyAll();
}
}
public void print3() throws InterruptedException {
synchronized(this) {
while(flag != 3) {
this.wait(); //线程3在此等待,if语句是在哪里等待,就在哪里起来
//while循环是循环判断,每次都会判断标记
}
System.out.print("方法三");
System.out.print("\r\n");
flag = 1;
//this.notify();
this.notifyAll();
}
}
}
消费者与生产者
//消费者
synchronized(对象) {
if(货物==null)
对象.wait();
对应的处理逻辑
}
//生产者
synchronized(对象) {
产生货物
对象.notifyAll();
}
线程间的通信还可以使用 管道输入/输出流
public class Piped {
public static void main(String[] args) throws Exception {
PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader();
// 将输出流和输入流进行连接,否则在使用时会抛出IOException
out.connect(in);
Thread printThread = new Thread(new Print(in), "PrintThread");
printThread.start();
int receive = 0;
try {
while ((receive = System.in.read()) != -1) {
out.write(receive);
}
} finally {
out.close();
}
}
static class Print implements Runnable {
private PipedReader in;
public Print(PipedReader in) {
this.in = in;
}
public void run() {
int receive = 0;
try {
while ((receive = in.read()) != -1) {
System.out.print((char) receive);
}
} catch (IOException ex) {
}
}
}
}