java之 ------ 多线程(从基础到加强)

首先了解线程的一些基本知识:

1、线程的定义:

是操作系统进程中能够独立执行的实体(控制流),是处理器调度和分派的基本单位。

2、线程的属性:

并发性、共享性、动态性和结构性

3、线程的状态

然后就是java与线程的调度:

1、主要是通过实现Runnable接口和继承Thread类来实现线程的调度和操作

a、Runnable接口(里面就一个run方法,只要通过重写run方法就可以实现自己想要的线程功能)

public interface Runnable 
{
    public abstract void run();
}
b、Thread线程类(继承这个类)

public class Thread extends Object implements Runnable
{
    public Thread()                           	//构造方法
    public Thread(String name)                 	//name指定线程名
    public Thread(Runnable target)         //target指定线程的目标对象
    public Thread(Runnable target, String name)

    public void run()                         	//描述线程操作的线程体
    public final String getName()               //返回线程名
    public final void setName(String name)     //设置线程名
    public static int activeCount()              //返回当前活动线程个数
    public static Thread currentThread()   //返回当前执行线程对象
    public Sting toString() 		//返回线程的字符串信息
    public void start()                               //启动已创建的线程对象
}

2、Thread.State类声明的线程状态,新建态、运行态、阻塞态和等待态、终止态


3、Thread类中改变和判断线程状态的方法

a、线程启动:

public void start()                            //启动线程对象
public final boolean isAlive()           //是否启动
b、线程睡眠:

public static void sleep(long millis) throws InterruptedException
c、线程中断:

public void interrupt()             //设置中断标记
public boolean isInterrupted()    //判断是否中断
public static boolean interrupted() //判断是否中断

4、线程对象的优先级(注:但是优先级高的不一定绝对先运行,也只能是同一时刻接收到不同线程时,优先级的高的先执行):

a、Thread类中声明了3个表示优先级的公有静态常量:

public static final int MIN__PRIORITY=1        	//最低优先级
public static final int MAX_PRIORITY=10       	//最高优先级
public static final int NORM_PRIORITY=5       	//默认优先级
b、Thread类中与线程优先级有关的方法2种:

public final int getPriority()                    	       //获得线程优先级
public final void setPriority(int newPriority)//设置线程优先级


5、定时器与图形动画设计

public class Timer implements Serializable
{
    public Timer(int delay, ActionListener l)
    public void addActionListener(ActionListener l)                    //注册定时事件监听器 
    public void setDelay(int delay)  //设置延时的时间间隔
    public void start()                        //启动定时器
    public void stop()                         //停止定时器
    public void restart()                     //重新启动定时器
}


例子1:
package p25.cooperate;

public class Buffer {
	private int value;
	public void put(int value){
		this.value = value;
	}
	public int get(){
		return this.value;
	}
}

class SendThread extends Thread{
	private Buffer buffer;
	public SendThread(Buffer buffer){
		this.buffer = buffer;
	}
	@Override
	public void run() {
		for(int i=1; i<=5; i++){
			buffer.put(i);
			System.out.println(this.getClass().getName()+"put:"+i);
		}
	}
	
}
class ReceiveThread extends Thread{
	private Buffer buffer;
	public ReceiveThread(Buffer buffer){
		this.buffer = buffer;
	}
	@Override
	public void run() {
		for(int i=1; i<=5; i++){
			int num = buffer.get();
			System.out.println("\t\t\t"+this.getClass().getName()+"get:"+num);
		}
	}
	
}
 class Run{
	 public static void main(String[] args) {
		Buffer buffer = new Buffer();
		(new SendThread(buffer)).start();
		(new ReceiveThread(buffer)).start();
	}
	 
 }
结果:

例子2:

package p24;

import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class WelcomeJFrame extends JFrame {

	public WelcomeJFrame(String[] texts){
		super("流动字");
		this.setBounds(300, 240, 500, 400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		if(texts==null || texts.length==0){
			this.getContentPane().add(new RollbyJPanel("Welcome!"));
		}else{
			this.getContentPane().setLayout(new GridLayout(texts.length, 1));
			for(int i=0;i<texts.length;i++){
				this.getContentPane().add(new RollbyJPanel(texts[i]));
			}
		}
		this.setVisible(true);
	}
	public WelcomeJFrame(){
		this(null);
	}
	
	private class RollbyJPanel extends JPanel implements Runnable,ActionListener{
		JTextField textWord,textSleep,textState;
		int sleeptime;
		JButton btnStart,btnInterrupt;
		Thread t;
		
		RollbyJPanel(String text) {
			this.setLayout(new GridLayout(2, 1));
			char[] space = new char[60];
			textWord = new JTextField(text+new String(space));
			this.add(textWord);
			JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
			this.add(p);
			p.add(new JLabel("sleep:"));
			this.sleeptime = (int) (Math.random()*100);
			textSleep = new JTextField(""+sleeptime,5);
			p.add(textSleep);
			textSleep.addActionListener(this);
			btnStart = new JButton("启动");
			p.add(btnStart);
			btnStart.addActionListener(this);
			btnInterrupt= new JButton("中断");
			p.add(btnInterrupt);
			btnInterrupt.addActionListener(this);
			t = new Thread(this);
			t.start();
			btnStart.setEnabled(false);
			
			p.add(new JLabel("state:"));
			textState = new JTextField(""+t.getState(),10);
			textState.setEditable(false);
			p.add(textState);
		}
		public void actionPerformed(ActionEvent e) {
			if(e.getSource()==textSleep){
				updateSleeptime();
				
			}
			if(e.getSource()==btnStart){
				updateSleeptime();
				t = new Thread(this);
				t.start();
				btnStart.setEnabled(false);
				btnInterrupt.setEnabled(true);
				textState.setText("运行ing...");
				//textState.setText(""+t.getState());	
			}
			if(e.getSource()==btnInterrupt){
				t.interrupt();
				btnStart.setEnabled(true);
				btnInterrupt.setEnabled(false);
				textState.setText(""+t.getState());				
			}
		}
		private void updateSleeptime() {
			try {
				sleeptime = Integer.parseInt(textSleep.getText());
				if(sleeptime<0){
					sleeptime=0;
					textSleep.setText(""+0);
				}
			} catch (NumberFormatException e1) {
				//e1.printStackTrace();
				JOptionPane.showMessageDialog(this, "\""+
				  textSleep.getText()+"\"不能转换成整数,请重新输入");
			}
		}
		public void run() {
			while (true) {
				try {
					String str = textWord.getText();
					str = str.substring(1) + str.substring(0, 1);
					textWord.setText(str);
					Thread.sleep(sleeptime);
				} catch (InterruptedException e) {
					break;//e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) {
         String[] strs={"Welcome","Hello","Rollby","aaaa","yyyy"};
         //new WelcomeJFrame(strs);
         new WelcomeJFrame();
         
	}

}
结果:

最后加强:

1、线程互斥锁(见java学习笔记-----多线程(交互线程))

a、多线程互斥共享“基本数据类型数据”资源:多线程的互斥共享资源----为基本数据类型时,不能直接当作互斥锁。解决方案:造一个与基本数据

类型资源平等的对象,来当作互斥锁。

b、多线程互斥共享“”资源

例子3:多窗口卖票

package p51;

public class ThreadSynDemo1 {

	public static void main(String[] args) {
		Thread t1 = new Thread(new Ticket("卖票窗口1"));
		Thread t2 = new Thread(new Ticket("卖票窗口2"));
		Thread t3 = new Thread(new Ticket("卖票窗口3"));
		Thread t4 = new Thread(new Ticket("卖票窗口4"));
		t1.start();
		t2.start();
		t3.start();
		t4.start();
//Person p1 = new Person("wjf",22,"jkkkj");
//Person p2 = new Person("yc",23,"aaaaa");
//p1.name   p2.name
//p1.room="a12345"; p2.room
/*
 * class Person{
 *    String name;
 *    int age;
 *    String info;
 *    static String room;
 * }
 * 
 */

		
	}

}

class Ticket implements Runnable {
	private static int num = 200;// 多线程的互斥共享资源----为基本数据类型时,不能直接当作互斥锁
	private static Object obj = new Object();// 解决方案:造一个与基本数据类型资源平等的对象,来当作互斥锁
	private String name;

	public Ticket(String name) {
		this.name = name;
	}

	public void run() {
		while (true) {
			synchronized (obj) { //※此处,obj不能用this来代替
				int ticketNum = num;// 拿到票号
				// 中间模拟一些卖票所需的手续,耗费时间随机
				int rand = (int) (Math.random() * 10);
				try {
					Thread.sleep(rand);
				} catch (Exception e) {
				}

				if (num > 0) {
					System.out.println(name + ":" + ticketNum);// 给出票
					num--;
				} else {
					break;
				}
			}
		}
	}
}

2、多线程调度与控制

a、java的多线程是抢占式的运行方式。

b、setPriority():设置优先级

package p51.schedule;

public class Schedule {
	//演示线程调度中的setPriority()方法
	//演示线程调度中的interrupt()方法
	public static void main(String[] args) {
		Thread t1 = new MyRunner();
		Thread t2 = new MyRunner();
		<span style="color:#ff0000;">t1.setPriority(9);
		t2.setPriority(3);</span>
		t1.start();
		t2.start();
		
		try{
			Thread.sleep(2000);
		}catch (Exception e) {
		}
		//t1.interrupt();//强制唤醒
		
	}

}

class MyRunner extends Thread{
	public void run(){
	   int i=0;
	   System.out.println(Thread.currentThread().getName()+"is sleep...");
	   try{
		   Thread.sleep(10000);
	   }catch (Exception e) {
	   }
	   for(;i<1000;i++){
		   System.out.println(Thread.currentThread().getName()+"No:"+i);
	   }
	}
}


c、sleep()方法:Thread类的sleep()方法对当前线程操作,是静态方法。sleep()的参数指定以毫秒为单位的线程休眠时间。除非因为中断而

提早恢复执行,否则线程不会在这段时间之前恢复执行。

package p51.schedule.s1;

public class Schedule {
    //演示线程调度中的sleep()方法
	public static void main(String[] args) {
		Thread t1 = new MyRunner(1);
		Thread t2 = new MyRunner(2);
		//t1.setPriority(9);
		//t2.setPriority(3);
		t1.start();
		t2.start();
		
	}

}

class MyRunner extends Thread{
	int num;
	public MyRunner(int num) {
		this.num = num;
	}

	public void run(){
	   int i=0;
	   System.out.println(Thread.currentThread().getName()+"is sleep...");
	   try{
		   if(num==1){
		      <span style="color:#ff0000;">Thread.sleep(5000);</span>
		   }
	   }catch (Exception e) {
	   }
	   for(;i<1000;i++){
		   System.out.println(Thread.currentThread().getName()+"No:"+i);
	   }
	}
}


d、interrupt()方法:一个线程可以调用另外一个线程的interrupt()方法,这将向暂停的线程发出一个InterruptedException。变相起到唤醒暂停

线程的功能。Thread类的方法interrupt(),是一种强制唤醒的技术。

package p51.schedule;

public class Schedule {
	//演示线程调度中的setPriority()方法
	//演示线程调度中的interrupt()方法
	public static void main(String[] args) {
		Thread t1 = new MyRunner();
		Thread t2 = new MyRunner();
		//t1.setPriority(9);
		//t2.setPriority(3);
		t1.start();
		t2.start();
		
		try{
			Thread.sleep(2000);
		}catch (Exception e) {
		}
		<span style="color:#ff0000;">t1.interrupt();//强制唤醒</span>
		
	}

}

class MyRunner extends Thread{
	public void run(){
	   int i=0;
	   System.out.println(Thread.currentThread().getName()+"is sleep...");
	   try{
		   Thread.sleep(10000);
	   }catch (Exception e) {
	   }
	   for(;i<1000;i++){
		   System.out.println(Thread.currentThread().getName()+"No:"+i);
	   }
	}
}


e、yield()方法:用来使具有相同优先级的线程获得执行的机会。如果具有相同优先级的其它线程是可运行的,yield()将把线程放到可运行池

中并使另一个线程运行。如果没有相同优先级的可运行线程,则什么都不做。

package p51.schedule.s2;

public class Schedule {
  //演示线程调度中的yield()方法
	public static void main(String[] args) {
		Thread t0 = new MyRunner(1);
		Thread t1 = new MyRunner(2);
		//t1.setPriority(6);//yield()方法只能把机会让给同一优先级的其它线程,此处即使优先级设成更高,先运行的概率反倒没有不设的高
		t0.start();
		t1.start();
	}

}

class MyRunner extends Thread{
	int num;
	public MyRunner(int num) {
		this.num = num;
	}

	public void run(){
	   int i=0;
	   System.out.println(Thread.currentThread().getName()+"is sleep...");
	   try{
		   Thread.sleep(1000);
		   if(num==1){
			   <span style="color:#ff0000;">Thread.yield()</span>;
			   num=0;
		   }
	   }catch (Exception e) {
	   }
	   for(;i<5;i++){
		   System.out.println(Thread.currentThread().getName()+"No:"+i);
	   }
	}
}


f、join()方法:调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行。它可以实现线程合并的功

能,经常用于线程的绝对调度。

package p51.schedule.s3;

public class Schedule {
  //演示线程调度中的join()方法
	public static void main(String[] args) {
		Thread t0 = new MyRunner(1);
		Thread t1 = new MyRunner(2);
		t0.start();
		try {
			<span style="color:#ff0000;">t0.join();</span>//把t0合并到当前线程(此处即main线程)
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		t1.start();
		
	}

}

class MyRunner extends Thread{
	int num;
	public MyRunner(int num) {
		this.num = num;
	}

	public void run(){
	   int i=0;
	   System.out.println(Thread.currentThread().getName()+"is sleep...");
	   try{
		   Thread.sleep(1000);
		   if(num==1){
			   Thread.yield();
			   num=0;
		   }
	   }catch (Exception e) {
	   }
	   for(;i<5;i++){
		   System.out.println(Thread.currentThread().getName()+"No:"+i);
	   }
	}
}


g、wait()方法:当前线程进入对象的wait pool。(例1的修改)

h、notify()/notifyAll()方法:唤醒对象的wait pool中的一个/所有等待线程。

package p25.cooperate.co;
public class LockBuffer {
	private int value;
	private boolean isEmpty=true;//信号量
	public synchronized void put(int value){//存放
		//if(!this.isEmpty){//非空  ※可能有bug,因为如果被意外唤醒,就往前执行了,不会重新判断信号量
		while(!this.isEmpty){//非空
			try {
				<span style="color:#ff0000;">this.wait();</span>
			} catch (InterruptedException e) {
			}
		}
		this.value = value;
		this.isEmpty = false;
		this.notify();
	}
	public synchronized int get(){
		while(this.isEmpty){//为空
			try {
				this.wait();
			} catch (InterruptedException e) {
			}
		}
		this.isEmpty=true;
		<span style="color:#ff0000;">this.notify();</span>
		return this.value;
	}
}


class SendThread extends Thread{
	private LockBuffer buffer;
	public SendThread(LockBuffer buffer){
		this.buffer = buffer;
	}
	@Override
	public void run() {
		for(int i=1; i<=5; i++){
			buffer.put(i);
			System.out.println(this.getClass().getName()+"put:"+i);
		}
	}
}
class ReceiveThread extends Thread{
	private LockBuffer buffer;
	public ReceiveThread(LockBuffer buffer){
		this.buffer = buffer;
	}
	@Override
	public void run() {
		for(int i=1; i<=5; i++){
			int num = buffer.get();
			System.out.println("\t\t\t"+this.getClass().getName()+"get:"+num);
		}
	}
}
 class Run{
	 public static void main(String[] args) {
		LockBuffer buffer = new LockBuffer();
		(new SendThread(buffer)).start();
		(new ReceiveThread(buffer)).start();
	}<pre name="code" class="java">

 结果:可以与上面的对面,就可以看出有什么不同了 
 

3、发牌器

package p25.cooperate.co;



import java.util.*; 
import java.awt.*;
import javax.swing.*;

public class CardJFrame extends JFrame
{
    public CardJFrame()
    {
        super("发牌程序");        
        this.setSize(460,200);
        this.setLocation(300,240);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        Container container=this.getContentPane();
        container.setLayout(new GridLayout(3,3));          //3行3列网格布局
        container.add(new JPanel());                       //网格布局的第1行第1列
        
        JTextArea text[]=new JTextArea[4];                 //显示牌号的4个文本区
        Font font=new Font("Helvetica", Font.PLAIN, 16);
        for (int i=0; i<text.length; i++)
        {
            text[i]=new JTextArea();
            text[i].setLineWrap(true);                     //设置文本区自动换行
            text[i].setEditable(false);
            text[i].setFont(font);
            container.add(text[i]);
            container.add(new JPanel());
        }
        this.setVisible(true);
        
        CardBuffer cardbuffer = new CardBuffer();
        Sender s = new Sender(cardbuffer,52);
        s.setPriority(10);                                 //设置最高优先级
        s.start();                                         //启动发牌线程    
        for (int i=0; i<text.length; i++)
            (new Receiver(cardbuffer,text[i],i)).start();  //创建并启动4个取牌线程,优先级为5
    }

    public static void main(String arg[])
    {
        new CardJFrame();
    }
}
class CardBuffer
{
    private int value;
    private boolean isEmpty=true;                //value是否空的信号量
    private int order=0;                         //信号量,约定取牌线程的次序
    
    synchronized void put(int i)
    {
        while (!isEmpty)                         //当value不空时,等待
            try
            {
                 this.wait();                    //等待
            }
            catch(InterruptedException e) {}
       
        value=i;                                 //当value空时,value获得值
        isEmpty=false;                           //设置value为不空状态
        notifyAll();                             //唤醒所有其他等待线程
    }
    
    synchronized int get(int order)              //order是取牌线程约定的次序
    {
        while (isEmpty || (this.order!=order))   //当value空或取牌次序不符时等待
            try
            {
                 this.wait();
            }
            catch(InterruptedException e) {}
        
        isEmpty=true;                            //设置value为空状态,并返回值
        notifyAll();
        this.order=(this.order+1)%4;             //加1使取牌次序轮转
        return value;                      
    }
}
/*
class Sender extends Thread                                //发牌线程类
{
    private CardBuffer cardbuffer;      
    private int count;                                     //牌数
    
    public Sender(CardBuffer cardbuffer,int count)
    {
        this.cardbuffer=cardbuffer;
        this.count=count;
    }
    
    public void run()
    {
        for (int i=1; i<=this.count; i++)
            cardbuffer.put(i);
    }
}*/

class Receiver extends Thread                    //取牌线程类
{
    private CardBuffer cardbuffer;
    private JTextArea text;
    private int order;                           //信号量,约定取牌线程的次序
    
    public Receiver(CardBuffer cardbuffer,JTextArea text,int order)
    {
        this.cardbuffer = cardbuffer ;
        this.text = text ;
        this.order = order;
    }
    
    public void run()
    {
        while(true)
        {
            text.append(" "+cardbuffer.get(this.order));
            try
            {
                 Thread.sleep(100);
            }
            catch(InterruptedException e) {}
        }
    }
}

//52  1-52  1-13黑  14-26红   27-39梅   40-52方   35%13
//a[52]  1-52
//for(int i=0;i<a.length;i++){
// a[i] <--> a[rand]
//}




class Sender extends Thread                                //发牌线程类
{
    private CardBuffer cardbuffer;      
    private int count;                                     //牌数
    private java.util.ArrayList<Integer> list;             //数组列表
    
    public Sender(CardBuffer cardbuffer,int count)
    {
        this.cardbuffer=cardbuffer;
        this.count=count;
        list=new ArrayList<Integer>();
        for (int i=1; i<=this.count; i++)
            list.add(new Integer(i));                      //列表中添加整数对象
        java.util.Random rand=new Random();                //随机数序列对象
        java.util.Collections.shuffle(list, rand);         //将列表的数据序列打散,按随机数序列
    }
    
    public void run()
    {
//        for (int i=1; i<=this.count; i++)
//            cardbuffer.put(((Integer)list.get(i-1)).intValue());
//或        
        Iterator<Integer> it=list.iterator();              //返回一个迭代器对象
        while (it.hasNext())                               //若有后继元素,使用迭代器遍历一个集合
            cardbuffer.put((Integer)it.next()); //返回后继元素
        
    }
}

结果:

一些重要相关概念:

1、创建线程和启动线程并不相同:在一个线程对新线程的Thread对象调用start()方法之前,这个线程并没有真正开始执行。Thread对象在其

线程真正启动之前就已经存在了,而且其线程退出之后仍然存在。因此,仍可以控制或获取关于已创建的线程的信息,即使线程还没有启动

或已经完成了。

2、结束线程:1)线程到达其run()方法的末尾,推荐这种方法,自然结束。

   2)线程抛出一个未捕获到的Exception或Error。

   3)另一个线程调用一个弃用的stop()方法。
3、守护程序线程(简称守护线程):我们提到过当Java程序的所有线程都完成时,该程序就退出,但这并不完全正确,因为程序中还隐藏的

系统线程。

注意的问题:

1、synchronized必须锁的是对象,基本数据类型的变量不能当作对象锁。

2、要保证多线程使用的是同一个互斥锁(对象锁),才能进行同步。

3、死锁的两种情况:1)多个线程共用同一个对象锁,互相等待。

      2)互相持有对方所需的资源(即每个线程都需要同时拿到多个资源才能继续执行,而多个线程都处于:各持有一部分,在等待另一部分。)

4、死锁的解决:要从设计方面去解决避免,即在设计时就考虑不能出现死锁。罗列出所有临界资源,画分布图,从图中观察其中的死锁情

况,改变其中线程的(临界)资源的获取方式。设计原则:尽量让程序中少出现临界资源。

5、wait/notify 和 sleep方法:wait和notify只能在它们被调用的实例的同步块内使用,而sleep()到处都可以用。wait()和sleep()最大的区别:

sleep()不释放对象锁,而wait()会释放,因此从效率方面考虑wait()方法更好。

6、 同步设计的基本原则: ◎ 同步块中(synchronized修饰)的代码越小越好!

◎ 同步块中不要写阻塞性代码(如,InputStream.read() )!

◎ 在持有锁的时候,不要对其它对象调用方法。(如果做到,可以消除最常见的死锁源头。)

7、同步概述:◎同步的原理:将需要同步的代码进行封装,并在该代码上加了一个锁。

◎同步的好处:解决多线程的安全问题。

◎同步的弊端:会降低性能。

◎同步的前提:必须要保证有多个线程且它们在同步中使用的是同一个锁。



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值