我的理解:
sleep 与 yield
优先级较高的获取时间片的概率较高,概率值从1~10
join 方法
只能在同步控制方法或同步控制块里调用wait(),notify()和notifyAll()。如果在非同步控制方法里调用这些方法,程序能通过编译,但运行的时候,将得到IllegalMonitorStateException异常,并伴随着一些含糊的消息,比如"当前线程不是拥有者"。消息的意思是,调用wait(),notify()和notifyAll()的线程在调用这些方法前必须"拥有"对象的锁。
synchronized(x){
x.notify();
}
class A extends Thread{
void run(){
sychrnoized(this){
xxx;
}
}
}
class B extends Thread{
void run(){
sychrnoized(this){
xxx;
}
}
}
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
对了,还有些底层的东西
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。
好了,来介绍各种方法的使用吧。
1.join 我最喜欢活雷锋了。
只有等另一个线程完成后自己的线程才开始执行。
public class Join {
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he,"线程");
demo.start();
for(int i=0;i<50;++i){
if(i>10){
try{
demo.join(); //强制执行demo
}catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("main 线程执行-->"+i);
}
}
static class hello implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}
}
执行后的结果为:
main 线程执行-->0
main 线程执行-->1
main 线程执行-->2
线程
main 线程执行-->3
线程
线程
main 线程执行-->4
main 线程执行-->5
main 线程执行-->6
.......
main 线程执行-->49
2.yield 这个家伙喜欢达尔文的哲学,谁强它就屈服
<pre name="code" class="java">public class H {
public static void main(String[] args) {
Runs runs1=new Runs();
Runs runs2=new Runs();
Thread t1=new Thread(runs1,"a");
Thread t2=new Thread(runs2,"b");
t1.setPriority(8);
t1.start();
t2.setPriority(5);
t2.start();
}
static class Runs implements Runnable{
public void run() {
if("a".equals(Thread.currentThread().getName())){
Thread.yield(); //yield只会将执行权给优先级高的线程
}
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
}
运行结果:
a--->0
a--->1
b--->0
b--->1
a--->2
b--->2
a--->3
b--->3
b--->4
b--->5
b--->6
b--->7
b--->8
b--->9
a--->4
a--->5
a--->6
a--->7
a--->8
a--->9
很明显,b的运行级别高,如果将倆者运行级别对换,那么运行结果自然是a快些 ,这是相对而言,并不是每一次结果都是那样,只是多次运行后的结果显示。
3.Interrupt 中断线程
这个不是很常用
public class Interrupt {
static class hello implements Runnable {
public void run() {
System.out.println("执行run方法");
try {
Thread.sleep(10000); /*睡十秒*/
System.out.println("线程完成休眠");
} catch (Exception e) {
System.out.println("休眠被打断");
return; //返回到程序的调用处
}
System.out.println("线程正常终止");
}
}
public static void main(String[] args) {
hello he = new hello();
Thread demo = new Thread(he, "线程");
demo.start();
try{
Thread.sleep(2000); /*主线程睡2秒*/
}catch (Exception e) {
e.printStackTrace();
}
demo.interrupt(); //2s后中断线程
}
}
结果:
执行run方法
休眠被打断
下面来俩个非常经典的例子来学习异步,锁这些概念。 我在网上找的资料,也是CSDN的吧,好像是博客园的,不管这么多了,下面的文字转自互联网
先来看一段程序:
public class Synchronized_I implements Runnable{
private int count=10; /*票数*/
public void run() {
for(int i=0;i<10;++i){
if(count>0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(count--);
}
}
}
public static void main(String[] args) {
Synchronized_I he=new Synchronized_I();
Thread h1=new Thread(he);
Thread h2=new Thread(he);
Thread h3=new Thread(he);
h1.start();
h2.start();
h3.start();
}
}
运行后的结果:
10
9
8
7
5
6
4
2
3
1
0
-1
很明显这是错误的,由于没有同步导致的,所谓同步,打个比方,假如中学时代打饭,一个窗口只能每次服务一个人吧。 而打饭的阿姨就是资源。 在这段代码就是篇数count
所谓同步就是在统一时间段中只有有一个线程运行,其他的线程必须等到这个线程结束之后才能继续执行。
同步的实现: 可以使用同步代码块和同步方法两种来完成
语法格式:
synchronized(同步对象){
//需要同步的代码
}
采用同步方法。
语法格式为synchronized 方法返回类型方法名(参数列表){
// 其他代码
}
第一次改进
public class Synchronized_II implements Runnable{
private int count=10; /*票数*/
public void run() {
for(int i=0;i<10;++i){
synchronized (this){ //在统一时间段中只有有一个线程运行
if(count>0){ //其他的线程必须等到这个线程结束之后才能继续执行
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(count--);
}
}
}
}
public static void main(String[] args) {
Synchronized_II he=new Synchronized_II();
Thread h1=new Thread(he);
Thread h2=new Thread(he);
Thread h3=new Thread(he);
h1.start();
h2.start();
h3.start();
}
}
运行结果:
10
9
8
7
6
5
4
3
2
1
第二次改进:
public class Synchronized_III implements Runnable{
private int count=10; /*票数*/
public void run() {
for(int i=0;i<10;i++){
sale();
}
}
public synchronized void sale() {
if(count>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count--);
}
}
public static void main(String[] args) {
Synchronized_III he=new Synchronized_III();
Thread h1=new Thread(he);
Thread h2=new Thread(he);
Thread h3=new Thread(he);
h1.start();
h2.start();
h3.start();
}
}
运行结果同上
但是需要注意的是:
提醒一下,当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。
那么怎么会死锁呢? 个人觉得就是你用了我的资源,我用了你的资源,俩个都在等对方的资源。
假如线程 “A”获得了刀,而线程“B”获得了叉。线程“A”就会进入阻塞状态来等待获得叉,而线程“B”则阻塞来等待“A”所拥有的刀。
高级工具
wait()和notify()、notifyAll()
这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。
synchronized关键字用于保护共享数据,阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,
如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢? 此时就用这三个方法来灵活控制。
wait()方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。
当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;
如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
注意 这三个方法都是java.lang.Object的方法。
下面列举经典的消费者和生产者的例子
公共资源Info.java
public class Info {
private String name = "Rollen";
private int age = 20;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Producer implements Runnable{
private Info info=null;
Producer(Info info){
this.info=info;
}
public void run(){
boolean flag=false;
for(int i=0;i<25;++i){
if(flag){ //如果标志为true的话,睡眠0.1秒后一切按照原来的那样,最后改为false
this.info.setName("Rollen");
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
this.info.setAge(20);
flag=false;
}else{ //名字改为chunGe,睡眠0.1秒后年龄也改
this.info.setName("chunGe");
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
this.info.setAge(100);
flag=true;
}
}
}
}
class Consumer implements Runnable{
private Info info=null;
public Consumer(Info info){
this.info=info;
}
public void run(){
for(int i=0;i<25;++i){
try{
Thread.sleep(100); /*睡眠0.1秒后输出用户信息*/
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(this.info.getName()+"<---->"+this.info.getAge());
}
}
}
public class Test {
public static void main(String[] args) {
Info info=new Info();
Producer pro=new Producer(info);
Consumer con=new Consumer(info);
new Thread(pro).start();
new Thread(con).start();
}
}
运行结果为:
chunGe<---->100
Rollen<---->100
chunGe<---->20
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
Rollen<---->100
chunGe<---->20
chunGe<---->100
很显然有点混乱了。 这样的话是因为资源还没操作完就被使用。没有同步所致,那么加入同步呢 ?
修改了info.java 新加了俩个同步资源的方法
public synchronized void set(String name, int age){
this.name=name;
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
this.age=age;
}
public synchronized void get(){
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(this.getName()+"<===>"+this.getAge());
}
在生产者中使用如下:
public void run(){
boolean flag=false;
for(int i=0;i<25;++i){
if(flag){ //如果标志为true的话,睡眠0.1秒后一切按照原来的那样,最后改为false
this.info.set("Rollen", 20);
flag = false;
}else{ //名字改为chunGe,睡眠0.1秒后年龄也改
this.info.set("ChunGe", 100);
flag = true;
}
}
}
运行结果为:
ChunGe<===>100
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
ChunGe<===>100
.........
这里出现了重复读取的问题
最后使出最强武器 : 等待和唤醒
public class Info {
private String name = "Rollen";
private int age = 20;
private boolean flag=false;
/**
* 同步资源,锁
* @param name
* @param age
*/
public synchronized void set(String name, int age){
if(!flag){
try{
super.wait();
}catch (Exception e) {
e.printStackTrace();
}
}
this.name=name;
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
this.age=age;
flag=false;
super.notify();
}
public synchronized void get(){
if(flag){
try{
super.wait();
}catch (Exception e) {
e.printStackTrace();
}
}
try{
Thread.sleep(100);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(this.getName()+"<===>"+this.getAge());
flag=true;
super.notify();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
运行结果正常:
运行流程如下:
当俩个线程开启后,因为标识符为false,所以set方法被调用,直接进入super.wait(),然后当前线程等待,会释放锁标志,从而synchronize get()方法可以执行,然后get方法执行完了之后执行了notify()方法,set方法被执行,开始修改info的属性。 标识符改变,周而复始,保持了资源访问的一致性。
在我看来,就是在同步函数中需要对资源访问和修改的代码前面加上wait,后面加上notify,表明这段代码像数据库中的事务一样,必须一起执行。
线程池我还没有深入去看,下一篇去学习。