java复习手册:多线程编程
进程与线程
多线程开发的本质是实质上是多个线程对同一类资源进行抢占。
Thread类实现多线程
package ali;
public class Demo01 {
public static void main(String[] args) {
new MyThread("小明").start();
new MyThread("小红").start();
}
}
class MyThread extends Thread{
private String title;
public MyThread(String title) {
this.title = title;
}
public void run() {
for(int i=0;i<10;++i) {
System.out.println(this.title + " " + i);
}
}
}
Runnable接口实现多线程
package ali;
public class Demo02 {
public static void main(String[] args) {
new Thread(new MyThread("票贩子A")).start();
new Thread(new MyThread("票贩子B")).start();
new Thread(new MyThread("票贩子C")).start();
}
}
class MyThread02 implements Runnable{
private String title;
public MyThread02(String title) {
this.title = title;
}
public void run() {
for(int i=0;i<10;++i) {
System.out.println(this.title + " " + i);
}
}
}
public class Demo01 {
public static void main(String[] args) {
new MyThread("小明").start();
new MyThread("小红").start();
}
}
class MyThread extends Thread{
private String title;
public MyThread(String title) {
this.title = title;
}
public void run() {
for(int i=0;i<10;++i) {
System.out.println(this.title + " " + i);
}
}
}
JNI技术
JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。 [1] 从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。
Thread 和 Runnable的关系
Thread 类的定义
public class Thread extends Object implements Runnable{
}
在使用Thread类启动多线程的时候,调用的是start()方法,而后找到的是run()方法,但通过Thread类的构造方法传递了一个Runnable接口对象的时候,那么该接口被Thread类中的target属性保存,在start()方法执行的时候,会调用Thread类中的run()方法,而这个run()方法去调用Runnable接口子类覆写过的run()方法。
Thread类主要描述的是线程,而Runnable主要描述的是资源
用一个买票程序来实现多个线程对同一资源的并发访问。
package ali;
public class Demo03 {
public static void main(String[] args) {
MyThread03 mt = new MyThread03();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
class MyThread03 implements Runnable{
private String title;
private int tickets = 10;
public void run() {
while(tickets>0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets-- + "张票");
}
}
}
Callable接口实现多线程
Runnable接口实现多线程有一个缺点,当线程执行完毕之后,无法获得一个返回值。在jdk1.5之后,提出了一个新的线程实现接口: java.util.concurrent.Callable
接口的定义:
public interface Callable<V>{
public V call() throws Exception{
}
}
可以设置一个泛型,泛型的类型就是返回数据的类型,这样的好处是可以避免向下转型带来的隐患问题。
线程运行状态
线程的命名和取得
package ali;
class MyThread05 implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
public class Demo05 {
public static void main(String[] args) {
MyThread05 mt = new MyThread05();
new Thread(mt,"A").start();
new Thread(mt).start();
new Thread(mt,"C").start();
mt.run();
//main
}
}
主线程负责整体运行,子线程负责耗时操作:
package ali;
public class Demo100 {
public static void main(String[] args) {
System.out.println("执行操作任务一");
// int temp = 0;
// for(int i=0;i<Integer.MAX_VALUE;++i) {
// temp+=i;
// }
new Thread(()->{
int temp1 = 0;
for(int i=0;i<Integer.MAX_VALUE;++i) {
temp1+=i;
}
}).start();
System.out.println("执行操作任务二");
System.out.println("执行操作任务三");
}
}
线程的休眠
如果希望某个线程可以暂缓一会的时候,可以进行休眠操作
在进行线程休眠的时候可能会造成中断异常
package ali;
public class Demo06 {
public static void main(String[] args) {
for(int i=0;i<5;++i) {
new Thread(()->{
for(int j=0;j<10;++j) {
System.out.println(Thread.currentThread().getName()+ " j = "+ j);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();;
}
}
}
线程中断
线程休眠的时候是会被其他线程打断的。
在Thread类里提供两种中断执行的处理方法:
1.判断线程是否被中断:
public boolean isInterrupted();
2.中断线程执行:
public void interrupt();
package ali;
public class Demo07 {
public static void main(String[] args) throws InterruptedException {
Thread td = new Thread(()->{
System.out.println("我需要休息十秒");
try {
Thread.sleep(10000);
System.out.println("我睡够了");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("我被吵起来了");
}
});
td.start();
Thread.sleep(1000);
if(!td.isInterrupted()) {
//
System.out.println("闹钟响了");
td.interrupt();
}
}
}
线程强制运行
线程的强制运行是指,当线程满足某些条件之后,某一个线程对象可以一直独占资源,直到该线程的程序执行结束。
public final void join() throws InterruptedException
package ali;
public class Demo08 {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Thread td = new Thread( ()->{
for(int i=0;i<100;++i) {
if(i==3) {
try {
mainThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 执行i= " + i);
}
},"玩耍的线程" );
td.start();
for(int i=0;i<100;++i) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("霸道的主线程"+" 执行i=" + i);
}
}
}
线程的礼让
线程的礼让是指线程先将资源让出去让别的线程执行。
定义为:
public final void yield()
package ali;
public class Demo08 {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Thread td = new Thread( ()->{
for(int i=0;i<100;++i) {
if(i % 10 == 0) {
Thread.yield();
System.out.println(" 玩耍的线程礼让执行");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 执行i= " + i);
}
},"玩耍的线程" );
td.start();
for(int i=0;i<100;++i) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("霸道的主线程"+" 执行i=" + i);
}
}
}
礼让执行的时候每一次调用yiled()方法,都只会礼让一次当前的资源
线程的优先级
从理论上来讲线程的优先级越高越有可能执行(越有可能抢占到资源)。
设置优先级:
public final void setPriority(int newPriority )
获取优先级:
public final int setPriority()
package ali;
public class Demo09 {
//private static final int MAX_PRIORITY = 0;
public static void main(String[] args) {
Thread t1 = new Thread( new MyThread09(),"小快" );
Thread t2 = new Thread( new MyThread09(),"小慢" );
t1.setPriority( Thread.MAX_PRIORITY);
t2.setPriority( Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
class MyThread09 implements Runnable{
public void run() {
for(int i=0;i<100;++i) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
同步问题引出
package ali;
public class Demo10 {
public static void main(String[] args) {
MyThread10 m1 = new MyThread10();
new Thread(m1,"票贩子A").start();
new Thread(m1,"票贩子B").start();
new Thread(m1,"票贩子C").start();
}
}
class MyThread10 implements Runnable{
private int tickets = 10;
public void run() {
while(true) {
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第 " + tickets-- + "张票 ");
}else {
break;
}
}
}
}
线程同步处理
同步问题的解决在于锁,指的是当某一个线程执行操作的时候,其他线程外面等待。
用synchronized 关键字来实现。
synchronized(同步对象){
同步代码块
}
package ali;
public class Demo11 {
public static void main(String[] args) {
MyThread11 m1 = new MyThread11();
new Thread(m1,"票贩子A").start();
new Thread(m1,"票贩子B").start();
new Thread(m1,"票贩子C").start();
}
}
class MyThread11 implements Runnable{
private int tickets = 10;
public void run() {
while(true) {
synchronized(this) {
if(this.tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第 " + tickets-- + "张票 ");
}else {
break;
}
}
}
}
}
加入同步之后,性能降低了。
可以同步方法,在方法 的定义上使用synchronized。
package ali;
public class Demo10 {
public static void main(String[] args) {
MyThread10 m1 = new MyThread10();
new Thread(m1,"票贩子A").start();
new Thread(m1,"票贩子B").start();
new Thread(m1,"票贩子C").start();
}
}
class MyThread10 implements Runnable{
private int tickets = 10;
public void run() {
while(true) {
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第 " + tickets-- + "张票 ");
}else {
break;
}
}
}
}