Java基础知识学了好久了,今天再拿来复习一下。
helloworld
先来一个helloworld热热身
Test.java
package cn.hncu.thread.hello;
public class Test {
public static void main(String[] args) {
MyThread mt1=new MyThread(0);//偶数
MyThread mt2=new MyThread(1); //奇数
mt1.start();
mt2.start();
MyRun mr1=new MyRun(50);
Thread t3=new Thread(mr1);
MyRun mr2=new MyRun(51);
Thread t4=new Thread(mr2);
t3.start();
t4.start();
}
}
MyThread.java
package cn.hncu.thread.hello;
public class MyThread extends Thread{
Integer first;
public MyThread(Integer first) {
this.first=first;
}
@Override
public void run() {
for(int i=first;i<50;i+=2){
System.out.print(i+" ");
}
System.out.println();
}
}
MyRun.java
package cn.hncu.thread.hello;
public class MyRun implements Runnable{
Integer first;
public MyRun(Integer first) {
this.first=first;
}
@Override
public void run() {
for(int i=first;i<100;i+=2){
System.out.print(i+" ");
}
System.out.println();
}
}
new出来了四个线程跑跑
多线程调度与控制
这里将通过四个版本代码来演示多线程调度与控制
v1版本
★ Java的多线程是抢占式的运行方式。 ★ setPriority()
★ sleep()方法
Thread类的sleep()方法对当前线程操作,是静态方法。sleep()的参数指定以毫秒为单位的线程休眠时间。除非因为中断而提早恢复执行,否则线程不会在这段时间之前恢复执行。
★ interrupt()方法
一个线程可以调用另外一个线程的interrupt()方法,这将向暂停的线程发出一个InterruptedException。变相起到唤醒暂停线程的功能。Thread类的方法interrupt(),是一种强制唤醒的技术。
Schedule.javapackage cn.hncu.thread.schedule.v1;
public class Schedule {
public static void main(String[] args) {
MyThread t1=new MyThread(0);
MyThread t2=new MyThread(1);
//setPriority() 相对调度
//t1.setPriority(5);
//t2.setPriority(6);
t1.start();
//t1.start(); //如果一个线程已经启动,再次启动会出异常java.lang.IllegalThreadStateException
t2.start();
//唤醒别人
t1.interrupt(); //mian 线程强制唤醒t1,如果t1正在sleep,那么就停止sleep,往后继续执行代码
}
}
MyThread.java
package cn.hncu.thread.schedule.v1;
public class MyThread extends Thread{
private Integer first;
public MyThread(Integer first) {
this.first=first;
}
@Override
public void run() {
if(first==0){
try {
Thread.sleep(1000); //主动睡1000毫秒=1s
} catch (InterruptedException e) {
System.out.println("我被强制唤醒了.......要继续干活了");
}
}
for(int i=first;i<50;i+=2){
System.out.print(i+" ");
}
System.out.println();
}
}
v2版本
★ yield()方法
用来使具有相同优先级的线程获得执行的机会。如果具有相同优先级的其它线程是可运行的,yield()将把线程放到可运行池中并使另一个线程运行。如果没有相同优先级的可运行线程,则什么都不做。
注意,执行一次yield()方法,该线程只是放弃当前这一次机会,然后又会重新和其它线程一起抢占CPU,很可能又比其它线程先抢到。
★ join()方法
调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行。它可以实现线程合并的功能,经常用于线程的绝对调度。
Schedule.javapackage cn.hncu.thread.schedule.v2;
public class Schedule {
public static void main(String[] args) {
MyThread t1=new MyThread(0);
MyThread t2=new MyThread(1);
//setPriority() 相对调度
t1.setPriority(6);
t2.setPriority(6);
t1.start();
try {
t1.join(); //把t1线程剩下的代码合并到当前线程来执行。即:把t1线程中剩下的代码在此处先执行完,再执行后面的代码
} catch (InterruptedException e) {
System.out.println("线程合并异常.....");
}
t2.start();
}
}
MyThread.java
package cn.hncu.thread.schedule.v2;
public class MyThread extends Thread{
private Integer first;
public MyThread(Integer first) {
this.first=first;
}
@Override
public void run() {
//if(first==0)
//yield(); //主动让一次,且把机会让给其他同优先级的线程,否则什么也不做
for(int i=first;i<50;i+=2){
System.out.print(i+" ");
}
System.out.println();
}
}
v3版本
★ wait()方法
当前线程进入对象的waitpool。
★notify()/notifyAll()方法
唤醒对象的waitpool中的一个/所有等待线程。
★suspend()、resume()和stop()这几个方法现在已经不提倡使用。
Schedule.javapackage cn.hncu.thread.schedule.v3;
public class Schedule {
public static void main(String[] args) {
Object obj=new Object();
MyThread t1=new MyThread(0,obj);
MyThread t2=new MyThread(1,obj);
t1.start();
t2.start();
}
}
MyThread.java
package cn.hncu.thread.schedule.v3;
public class MyThread extends Thread{
private Integer first;
private Object obj;
private static boolean isWait=true;
public MyThread(int first, Object obj) {
this.first=first;
this.obj=obj;
}
@Override
public void run() {
synchronized(obj){
for(int i=first;i<50;i+=2){
if(first==1&&i>25&&isWait){
isWait=false;
try {
obj.wait();
} catch (InterruptedException e) {
System.out.println("被唤醒了.......");
}
}
System.out.print(i+" ");
}
System.out.println();
if(first==0){
isWait=false; //为防止0线程先行拿到锁执行完毕以后,1线程一直wait
obj.notify(); //只通知那些和自己处于同一等待池中的线程(被同一对象锁困住),任选一个
//obj.notifyAll();//唤醒池中所有等待的线程
}
} //还锁
}
}
v4版本
Schedule.java
package cn.hncu.thread.schedule.v4;
public class Schedule {
public static void main(String[] args) {
MyRun r=new MyRun();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
MyRun.java
package cn.hncu.thread.schedule.v4;
public class MyRun implements Runnable{
private boolean isWait=true;
@Override
public void run() {
synchronized(this){
for(int i=0;i<100;i+=2){
if(i>50&&isWait){
isWait=false;
try {
wait(); //会释放锁
} catch (InterruptedException e) {
System.out.println("被唤醒了....");
}
}
System.out.print(i+" ");
}
System.out.println();
notify();
} //还锁
}
}
一个线程跑到50就等待,另外一个线程开始跑
线程互斥加强(互斥锁)
★带互斥的共享栈
多线程互斥共享“栈”资源
Client.javapackage cn.hncu.thread.shareStack;
public class Client {
public static void main(String[] args) {
MyStack ms=new MyStack();
PushThread push=new PushThread(ms);
PopThread pop=new PopThread(ms);
push.start();
pop.start();
}
}
MyStack.java
package cn.hncu.thread.shareStack;
public class MyStack {
int idx=0;
char data[]=new char[6];
public void push(char ch){
//同步块 对象直接指定
synchronized(this){ //用this当对象锁
data[idx++]=ch;
System.out.println("push :"+ch);
this.notify();
}
}
//同步方法: 对象锁是该方法的拥有者 ---静态方法是类,非静态方法是this对象
public synchronized char pop(){ //this对象锁
if(idx<1){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("pop :"+data[--idx]);
return data[idx];
}
}
PushThread.java
package cn.hncu.thread.shareStack;
public class PushThread extends Thread{
private MyStack stack;
public PushThread(MyStack stack) {
this.stack=stack;
}
@Override
public void run() {
for(int i=65;i<70;i++){
stack.push((char) i);
}
}
}
PopThread.java
package cn.hncu.thread.shareStack;
public class PopThread extends Thread{
private MyStack stack;
public PopThread(MyStack stack) {
this.stack=stack;
}
@Override
public void run() {
for(int i=65;i<70;i++){
stack.pop();
}
}
}
★多窗口卖票
多线程互斥共享“基本数据类型数据”资源
版本一TicketSale.java
package cn.hncu.thread.ticket.v1;
public class TicketSale {
public static void main(String[] args) {
//开四个窗口 让它们都开始卖票(启动线程)
TicketWindow tw=new TicketWindow("窗口 ");
Thread t1=new Thread(tw);
Thread t2=new Thread(tw);
Thread t3=new Thread(tw);
Thread t4=new Thread(tw);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
TicketWindow.java
package cn.hncu.thread.ticket.v1;
public class TicketWindow implements Runnable {
private Integer num = 200; // 共享变量(基本数据类型的数据) 不能当对象锁---因为他不是对象
private Object obj = new Object();// 可以新建一个与基本数据类型共享变量平行的对象,来替代它当锁
private String name;
public TicketWindow(String name) {
this.name = name;
}
@Override
public void run() {
synchronized (obj) { // 拿锁--对象锁
// synchronized(this){ //直接用this作对象锁也是可以的----非静态变量的对象锁是可以用this替代
while (true) {
if (num> 0) {
System.out.println(name +": " + num);
num--;
}else{
break;
}
}
}//解锁---还锁
}
}
//非静态变量要当对象锁时,直接用this代替(不需要再另外造对象),无论该变量是否为对象
版本二
TicketSale.java
package cn.hncu.thread.ticket.v2;
public class TicketSale {
public static void main(String[] args) {
//开四个窗口 让它们都开始卖票(启动线程)
Thread t1=new Thread(new TicketWindow("窗口1 "));
Thread t2=new Thread(new TicketWindow("窗口2 "));
Thread t3=new Thread(new TicketWindow("窗口3 "));
Thread t4=new Thread(new TicketWindow("窗口4 "));
t2.start();
t1.start();
t4.start();
t3.start();
}
}
TicketWindow.java
package cn.hncu.thread.ticket.v2;
public class TicketWindow implements Runnable {
private static Integer num = 200; // 共享变量(基本数据类型的数据) 不能当对象锁---因为他不是对象
private static Object obj = new Object();// 可以新建一个与基本数据类型共享变量平行的对象,来替代它当锁
private String name;
public TicketWindow(String name) {
this.name = name;
}
@Override
public void run() {
while(true){
synchronized (obj) {//※静态变量的对象锁是不能用this来代替的
if (num > 0) {
System.out.println(name + ":" + num);
num--;
} else {
break;
}
}
}
}
}
两个版本一个用了非静态变量作锁,一个用了静态变量作锁,两者还是有差别的。