java多线程
继承Thread:
/*
线程的实现:继承Thread 重写run 方法,start 启动线程
*/
public class MyThread extends Thread {
@Override
public void run() {
for (int i=0;i<5;i++){
System.out.println(MyThread.this.toString()+":"+i);
}
}
@Test
public void test(){
Thread thread = new MyThread();
thread.start();
}
}
执行结果:
Thread[Thread-1,5,main]:0
Thread[Thread-1,5,main]:1
Thread[Thread-1,5,main]:2
Thread[Thread-1,5,main]:3
Thread[Thread-1,5,main]:4
实现Runnable接口:
/*
多线程的实现:通过实现Runnable接口 覆写 run 方法,再用Thread 启动
*/
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i=0;i<5;i++){
System.out.println(MyRunnable.this.toString()+":"+i);
}
}
@Test
public void test(){
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
执行结果:
Base.thread.com.MyRunnable@1ff4714f:0
Base.thread.com.MyRunnable@1ff4714f:1
Base.thread.com.MyRunnable@1ff4714f:2
Base.thread.com.MyRunnable@1ff4714f:3
Base.thread.com.MyRunnable@1ff4714f:4
实现Callable接口:
/*
虽然 Runnable 可以避免单继承的局限性,但是该接口不能返回操作结果。
故从 jdkl.5 开始 有了Callable 接口。
但是必须要用Thread类来启动线程,而Thread类里没有定义任何构造方法可以直接接受Callable接口对象实例,
故,从jdk1.5 开始,提供了一个FutureTask<V> 的类
*/
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i=0;i<5;i++){
System.out.println(MyCallable.this.toString()+":"+i);
}
return "success";
}
@Test
public void test() throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<String>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
System.out.println(result);
}
}
执行结果:
Base.thread.com.MyCallable@6f961649:0
Base.thread.com.MyCallable@6f961649:1
Base.thread.com.MyCallable@6f961649:2
Base.thread.com.MyCallable@6f961649:3
Base.thread.com.MyCallable@6f961649:4
success
多线程常用的操作方法
线程的命名与取得
方法 | 类型 | 描述 |
---|---|---|
public Thread(Runnable tatget, String name) | 构造 | 实例化线程对象,接收Runnable接口子类对象,同时设置线程名称 |
public final void setName(String name) | 普通 | 设置线程名字 |
public final String getName() | 普通 | 取得线程名字 |
线程的休眠
Thread.sleep(millis); //毫秒
线程优先级
线程优先级操作:
方法或常量 | 类型 | 描述 |
---|---|---|
public static final int MAX_PRIORITY | 常量 | 最高优先级,value = 10 |
public final static int NORM_PRIORITY | 常量 | 中等优先级,value = 5 |
public final static int MIN_PRIORITY | 常量 | 最低优先级,value = 1 |
public final void setPriority(int newPriority) | 普通 | 设置线程优先级 |
public final int getPriority() | 普通 | 取得线程优先级 |
线程的同步与死锁
同步问题的引出
卖票 :
public class TicketRunnable implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int i = 0;i<20;i++){
if (ticket >0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,ticket =" + this.ticket--);
}
}
}
}
public class TestDemo {
public static void main(String[] args) {
Runnable ticketThread = new TicketRunnable();
new Thread(ticketThread,"票贩子A").start();
new Thread(ticketThread,"票贩子B").start();
new Thread(ticketThread,"票贩子C").start();
new Thread(ticketThread,"票贩子D").start();
}
}
执行结果:
票贩子D卖票,ticket =5
票贩子A卖票,ticket =3
票贩子B卖票,ticket =4
票贩子C卖票,ticket =5
票贩子D卖票,ticket =2
票贩子B卖票,ticket =1
票贩子C卖票,ticket =2
票贩子A卖票,ticket =0
注意:每次执行结果均可能不同,但是会出现同一张票卖了多次的,甚至出现 ticket 为负数的情况
这就是同步问题.解决方案:
- 同步代码块:利用synchronized 包装代码块,但是需要指定同步对象,一般设置为this
- 同步方法:利用synchronize定义方法
同步代码块:
public void run() {
for (int i = 0;i<20;i++){
synchronized (this){
if (ticket >0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,ticket =" + this.ticket--);
}
}
}
}
同步方法:
public void run() {
for (int i = 0;i<20;i++){
sale();
}
}
private synchronized void sale() {
if (ticket >0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,ticket =" + this.ticket--);
}
}