Java学习笔记14

======================================================================
   
======================================================================

======================================================================
   java
第十四天
======================================================================

(1) 线程的同步

进程是一套顺序执行的指令。

同时开辟并行执行序列即多线程          run() 结束,线程就结束

JVM就是一个进程,在JVM中分出线程

进程数据空间独立;线程数据空间共享, 线程间通信更容易

分配的方式不如线程好,能充分使用CPU的资源。 共享数据就要加锁、解锁,会降低效率。

 

OS 时分共享操作系统 实时操作系统

时分共享操作系统    时间片    调度系统把每个时间片分给多个进程   性能调优是根据时间片划分, 时间片会影响整个操作系统。

 

为了让某些工作并行,在进程主线程中开辟多个线程,这就是线程的机制。

 

 

我们关心的是被线程共享的数据,主要看synchronized加在何处。往往不加在线程里面,而是在共享的对象上。

 

只有运行状态的线程才有机会执行代码!

只有等到所有线程终止,进程才结束!

当一个对象分配给某线程锁标记时,其它线程不能访问同步方法,但能访问非同步方法!

 

起线程的两种方式:1、继承Thread,覆盖run()方法,用start()启动

                  2、实现Runnable接口,用来构造线程对象。(线程对象不是线程,但代表一个线程)

当多线程并发访问同一个对象(临界资源)的时候,如果原子操作被打破,则会造成数据的不一致.
       
java,用给临界资源加锁的机制来解决这个问题
.
       
每一个对象都有一个互斥锁标记(monitor),只能分配给一个线程(对象相当于女孩,每个女孩都有一个锁标记,并且这个锁标记只能给一个男孩(线程))
.
       synchronized(o[
对象的引用]){O对象加锁的同步代码块
}
       synchronized
可以作为一个方法的修饰符,对当前对象加锁
.
       
例子
:
       public synchronized void m(){}
       public void m(){
           synchronized(this){}//
对当前对象的方法加锁
.
       }
       
每一个对象都有一个锁池,它里面装的是等待该对象锁标记的线程.(就象每个女孩都有一个锁池,里面放着等待要这个女孩锁标记的男孩(线程))

       
一个线程可以拥有多个对象的标记.同步代码块是可以嵌套的(一个男孩可以有多个女孩的锁标记,但是一个女孩只能有一个男朋友)换句话说就是(一个线程可以拿到多个对象的锁标记,但是一个对象的锁标记只能给一个线程。).
       synchronized(o1){
           synchronized(o2){ //
一个线程可以拥有多个对象的锁标记
.
               t1;
           }
       }
       
当一个对象拥有一个对象的锁标记的时候,在另一个对象的锁池等待的时候不会释放拥有的锁(一个男孩在拥有了一个女孩的锁标记的时候,还在等待另一个女孩的锁标记,在等待的时候不会释放上一个女孩的锁标记。)
.
       
当一个拥有一个对象的锁标记的线程访问这个对象的时候,其他的对象不能访问这个对象的同步方法,只能访问这个对象的非并发方法.(当一个女孩发出锁标记的时候,就是有了男朋友,这个对象就被加了锁,她定义的同步方法(拥抱,接吻,#%&%$#)只能这个男朋友访问,其他男孩不能访问,其他的男孩只能访问这个女孩的非加锁方法)呵呵
.
       
当一个线程阻塞在一个对象的锁池里的时候,不会释放其所拥有的其他对象的的锁标记

       
每一个线程不释放自己拥有的资源,却申请别的线程,会造成死锁。

-------------------------------------------------------------------------------
   (3)
解决死锁问题 进程间通信
   (4)
每个对象有一个等待队列的空间.
   (5)
等待机制
:
       调用wait()方法的条件,必需在这个同步代码块中.
       
当调用wait()方法的时候

       
线程t1: o.wait();
           
前提: 必需在对o加锁的同步代码块里
.
           
结果
:
               1: t1
会释放器所拥有的所有的锁标记
.
               2: t1
会进入o的等待队列
.
   (6)
通知机制
       线程t2:  o.notify()/o.notifyAll(); [线程执行这个代码,全当是发善心,因为他可以把其他线程从井里面救出来,也可以不救出来]
           
前提: 必需在对o加锁的同步代码块里
.
           
结果: 1: t2会从o的等待队列中释放一个线程/所有线程.

wait() sleep()的联系和区别:

1. wait()是从Object继承下来的方法,而sleep()Thread中的静态方法

2. wait() sleep()都要阻塞运行,释放CPU资源

3. wait()要释放锁; 而sleep()不释放锁

wait()方法被调用时会解除锁定,但是我们能使用它的地方只是在一个同步方法或代码块内。


   
锁池: 是被迫进入,
   
等待队列: 是一个(),在进去的时候把所有的资源释放,怎么才能出去,别人把你救出去.别人调用
notify()/notifyAll()
   
进程之间进行通信需要一个标记值来调度两个线程.
   例子: (生产者和消费者的例子)
 
public class TestPC {

    public static void main(String[] args) {

        MyStack stack=new MyStack();

        PushThread t1=new PushThread(stack);

        PushThread2 t3=new PushThread2(stack);

        PopThread t2=new PopThread(stack);   

        t1.start();//其中一个生产者.

        t3.start();//另一个生产者.

        t2.start();//消费者.

    }

}   

class MyStack{//临界资源,要加锁。

    char[] data=new char[6];

    int index;//需要全局属性来调度.

    public synchronized void push(char c){

        if(data.length==index){

            try {

                this.wait();//释放自己所有的资源,包括自己拥有的锁.这样其他的线程就可以访问这个锁定的方法.

            } catch (InterruptedException e) {

            }

        }

        System.out.println(c+" pushed!!");

        data[index]=c;

        index++;//标记值加1.调度两个线程.

        this.notifyAll();//从等待队列中把等待操作这个对象的线程调出来,把它从等待状态变成锁池状态.

        this.print();//打印栈中的元素。

    }

    public synchronized void pop(){

        if(index==0){

            try {

                this.wait();//释放自己所有的资源,包括自己拥有的锁.这样其他的线程就可以访问这个锁定的方法.

            } catch (InterruptedException e) {

            }

        }

        index--;//标记值减1;

        System.out.println(data[index]+" poped!!");

        data[index]=' ';           

        this.notifyAll();

        this.print();

    }

    public void print(){

        for(int i=0;i<index;i++){

            System.out.print(data[i]+" ");   

        }

        System.out.println();

    }

}   

class PushThread extends Thread{//操作这个栈的一个线程(入栈)。

    MyStack stack;

   

    public PushThread(MyStack stack) {

        this.stack = stack;

    }

 

    public void run(){

        for(char i='A';i<'Z';i++){

            stack.push(i);//调用入栈方法.

            System.out.println("PushThread1 ");

        }

       

    }

}   

class PopThread extends Thread{//操作这个栈的另一个线程(出栈)。

    MyStack stack;   

    public PopThread(MyStack stack) {

        this.stack = stack;

    }   

    public void run(){

        for(char i='A';i<'Z';i++){

            stack.pop();//调用出栈方法.

        }

    }

}   

class PushThread2 extends Thread{

    MyStack stack;

 

    public PushThread2(MyStack stack) {

        this.stack = stack;

    }

    public void run(){

        for(char i='A';i<'Z';i++){

            stack.push(i);

            System.out.println("PushThread2");

        }           

    }       

}

 

----------------------------------------------------------------------------
   
作业:

新思路:必要时可利用Object中的 锁标记锁池等待队列 用其wait() / notify() 方法,

       自己制造临界资源来进行线程间通讯。

      (参考字母数字交叉打印的例子TestNumberCharPrint.java

       其中注意o.notifyAll() o.wait() 的调用顺序,还要注意边界问题,体现在最后要来一个判断if(c!=’Z’)  o.wait(); 进而让程序正常结束。


 
public class HomeWork {

    public static void main(String[] args) {

        Run r = new Run();

        Thread1 t1 = new Thread1(r);

        Thread2 t2 = new Thread2(r);

        t1.start();

        t2.start();

    }

}

class Run {

    int index = 1;

    public synchronized void printNum() {

        for (int i = 1; i <= 52; i++) {

            while (index % 3 == 0) {

                try {

                    this.wait();

                } catch (InterruptedException e) {

                }

            }

            System.out.print(i + " ");

            index++;

            this.notifyAll();

        }

    }

    public synchronized void printChar() {

 

        for (char j = 'A'; j < 'Z'; j++) {

            while (index % 3 != 0) {

                try {

                    this.wait();

                } catch (InterruptedException e) {

                }

            }

            index++;

            System.out.print(j + " ");

            this.notifyAll();

        }

    }

}

class Thread1 extends Thread {

    Run r;

 

    public Thread1(Run r) {

        this.r = r;

    }

    public void run() {

        r.printNum();

    }

}

class Thread2 extends Thread {

    Run r;

    public Thread2(Run r) {

        this.r = r;

    }

    public void run() {

        r.printChar();

    }

}

 

 

(1) 线程的同步

进程是一套顺序执行的指令。

同时开辟并行执行序列即多线程          run() 结束,线程就结束

JVM就是一个进程,在JVM中分出线程

进程数据空间独立;线程数据空间共享, 线程间通信更容易

分配的方式不如线程好,能充分使用CPU的资源。 共享数据就要加锁、解锁,会降低效率。

 

OS 时分共享操作系统 实时操作系统

时分共享操作系统    时间片    调度系统把每个时间片分给多个进程   性能调优是根据时间片划分, 时间片会影响整个操作系统。

 

为了让某些工作并行,在进程主线程中开辟多个线程,这就是线程的机制。

 

 

我们关心的是被线程共享的数据,主要看synchronized加在何处。往往不加在线程里面,而是在共享的对象上。

 

只有运行状态的线程才有机会执行代码!

只有等到所有线程终止,进程才结束!

当一个对象分配给某线程锁标记时,其它线程不能访问同步方法,但能访问非同步方法!

 

起线程的两种方式:1、继承Thread,覆盖run()方法,用start()启动

                  2、实现Runnable接口,用来构造线程对象。(线程对象不是线程,但代表一个线程)

当多线程并发访问同一个对象(临界资源)的时候,如果原子操作被打破,则会造成数据的不一致.
       
java,用给临界资源加锁的机制来解决这个问题
.
       
每一个对象都有一个互斥锁标记(monitor),只能分配给一个线程(对象相当于女孩,每个女孩都有一个锁标记,并且这个锁标记只能给一个男孩(线程))
.
       synchronized(o[
对象的引用]){O对象加锁的同步代码块
}
       synchronized
可以作为一个方法的修饰符,对当前对象加锁
.
       
例子
:
       public synchronized void m(){}
       public void m(){
           synchronized(this){}//
对当前对象的方法加锁
.
       }
       
每一个对象都有一个锁池,它里面装的是等待该对象锁标记的线程.(就象每个女孩都有一个锁池,里面放着等待要这个女孩锁标记的男孩(线程))

       
一个线程可以拥有多个对象的标记.同步代码块是可以嵌套的(一个男孩可以有多个女孩的锁标记,但是一个女孩只能有一个男朋友)换句话说就是(一个线程可以拿到多个对象的锁标记,但是一个对象的锁标记只能给一个线程。).
       synchronized(o1){
           synchronized(o2){ //
一个线程可以拥有多个对象的锁标记
.
               t1;
           }
       }
       
当一个对象拥有一个对象的锁标记的时候,在另一个对象的锁池等待的时候不会释放拥有的锁(一个男孩在拥有了一个女孩的锁标记的时候,还在等待另一个女孩的锁标记,在等待的时候不会释放上一个女孩的锁标记。)
.
       
当一个拥有一个对象的锁标记的线程访问这个对象的时候,其他的对象不能访问这个对象的同步方法,只能访问这个对象的非并发方法.(当一个女孩发出锁标记的时候,就是有了男朋友,这个对象就被加了锁,她定义的同步方法(拥抱,接吻,#%&%$#)只能这个男朋友访问,其他男孩不能访问,其他的男孩只能访问这个女孩的非加锁方法)呵呵
.
       
当一个线程阻塞在一个对象的锁池里的时候,不会释放其所拥有的其他对象的的锁标记

       
每一个线程不释放自己拥有的资源,却申请别的线程,会造成死锁。

-------------------------------------------------------------------------------
   (3)
解决死锁问题 进程间通信
   (4)
每个对象有一个等待队列的空间.
   (5)
等待机制
:
       调用wait()方法的条件,必需在这个同步代码块中.
       
当调用wait()方法的时候

       
线程t1: o.wait();
           
前提: 必需在对o加锁的同步代码块里
.
           
结果
:
               1: t1
会释放器所拥有的所有的锁标记
.
               2: t1
会进入o的等待队列
.
   (6)
通知机制
       线程t2:  o.notify()/o.notifyAll(); [线程执行这个代码,全当是发善心,因为他可以把其他线程从井里面救出来,也可以不救出来]
           
前提: 必需在对o加锁的同步代码块里
.
           
结果: 1: t2会从o的等待队列中释放一个线程/所有线程.

wait() sleep()的联系和区别:

1. wait()是从Object继承下来的方法,而sleep()Thread中的静态方法

2. wait() sleep()都要阻塞运行,释放CPU资源

3. wait()要释放锁; 而sleep()不释放锁

wait()方法被调用时会解除锁定,但是我们能使用它的地方只是在一个同步方法或代码块内。


   
锁池: 是被迫进入,
   
等待队列: 是一个(),在进去的时候把所有的资源释放,怎么才能出去,别人把你救出去.别人调用
notify()/notifyAll()
   
进程之间进行通信需要一个标记值来调度两个线程.
   例子: (生产者和消费者的例子)
 
public class TestPC {

    public static void main(String[] args) {

        MyStack stack=new MyStack();

        PushThread t1=new PushThread(stack);

        PushThread2 t3=new PushThread2(stack);

        PopThread t2=new PopThread(stack);   

        t1.start();//其中一个生产者.

        t3.start();//另一个生产者.

        t2.start();//消费者.

    }

}   

class MyStack{//临界资源,要加锁。

    char[] data=new char[6];

    int index;//需要全局属性来调度.

    public synchronized void push(char c){

        if(data.length==index){

            try {

                this.wait();//释放自己所有的资源,包括自己拥有的锁.这样其他的线程就可以访问这个锁定的方法.

            } catch (InterruptedException e) {

            }

        }

        System.out.println(c+" pushed!!");

        data[index]=c;

        index++;//标记值加1.调度两个线程.

        this.notifyAll();//从等待队列中把等待操作这个对象的线程调出来,把它从等待状态变成锁池状态.

        this.print();//打印栈中的元素。

    }

    public synchronized void pop(){

        if(index==0){

            try {

                this.wait();//释放自己所有的资源,包括自己拥有的锁.这样其他的线程就可以访问这个锁定的方法.

            } catch (InterruptedException e) {

            }

        }

        index--;//标记值减1;

        System.out.println(data[index]+" poped!!");

        data[index]=' ';           

        this.notifyAll();

        this.print();

    }

    public void print(){

        for(int i=0;i<index;i++){

            System.out.print(data[i]+" ");   

        }

        System.out.println();

    }

}   

class PushThread extends Thread{//操作这个栈的一个线程(入栈)。

    MyStack stack;

   

    public PushThread(MyStack stack) {

        this.stack = stack;

    }

 

    public void run(){

        for(char i='A';i<'Z';i++){

            stack.push(i);//调用入栈方法.

            System.out.println("PushThread1 ");

        }

       

    }

}   

class PopThread extends Thread{//操作这个栈的另一个线程(出栈)。

    MyStack stack;   

    public PopThread(MyStack stack) {

        this.stack = stack;

    }   

    public void run(){

        for(char i='A';i<'Z';i++){

            stack.pop();//调用出栈方法.

        }

    }

}   

class PushThread2 extends Thread{

    MyStack stack;

 

    public PushThread2(MyStack stack) {

        this.stack = stack;

    }

    public void run(){

        for(char i='A';i<'Z';i++){

            stack.push(i);

            System.out.println("PushThread2");

        }           

    }       

}

 

----------------------------------------------------------------------------
   
作业:

新思路:必要时可利用Object中的 锁标记锁池等待队列 用其wait() / notify() 方法,

       自己制造临界资源来进行线程间通讯。

      (参考字母数字交叉打印的例子TestNumberCharPrint.java

       其中注意o.notifyAll() o.wait() 的调用顺序,还要注意边界问题,体现在最后要来一个判断if(c!=’Z’)  o.wait(); 进而让程序正常结束。


 
public class HomeWork {

    public static void main(String[] args) {

        Run r = new Run();

        Thread1 t1 = new Thread1(r);

        Thread2 t2 = new Thread2(r);

        t1.start();

        t2.start();

    }

}

class Run {

    int index = 1;

    public synchronized void printNum() {

        for (int i = 1; i <= 52; i++) {

            while (index % 3 == 0) {

                try {

                    this.wait();

                } catch (InterruptedException e) {

                }

            }

            System.out.print(i + " ");

            index++;

            this.notifyAll();

        }

    }

    public synchronized void printChar() {

 

        for (char j = 'A'; j < 'Z'; j++) {

            while (index % 3 != 0) {

                try {

                    this.wait();

                } catch (InterruptedException e) {

                }

            }

            index++;

            System.out.print(j + " ");

            this.notifyAll();

        }

    }

}

class Thread1 extends Thread {

    Run r;

 

    public Thread1(Run r) {

        this.r = r;

    }

    public void run() {

        r.printNum();

    }

}

class Thread2 extends Thread {

    Run r;

    public Thread2(Run r) {

        this.r = r;

    }

    public void run() {

        r.printChar();

    }

}

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

炼丹狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值