首先了解线程的一些基本知识:
1、线程的定义:
是操作系统进程中能够独立执行的实体(控制流),是处理器调度和分派的基本单位。
2、线程的属性:
并发性、共享性、动态性和结构性
3、线程的状态![](https://img-blog.csdn.net/20150807234124381?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
然后就是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、同步概述:◎同步的原理:将需要同步的代码进行封装,并在该代码上加了一个锁。
◎同步的好处:解决多线程的安全问题。
◎同步的弊端:会降低性能。
◎同步的前提:必须要保证有多个线程且它们在同步中使用的是同一个锁。