多线程基础0:多线程创建方式
实现多线程的方式(runnable与Thread)
Thread 和 Runnable 的相同点:都是“多线程的实现方式”。
Thread 和 Runnable 的不同点:
Thread 是类,而Runnable是接口;Thread本身是实现了Runnable接口的类。我们知道“一个类只能有一个父类,
但是却能实现多个接口”,因此Runnable具有更好的扩展性。
此外,Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,
它们会共享Runnable对象上的资源。通常,建议通过“Runnable”实现多线程!
runable的方式来创建多线程
package com.example;
public class MyThread implements Runnable{
private int ticket=10;
@Override
public void run() {
for (int i=0;i<20;i++){
if(this.ticket>0){
System.out.println(Thread.currentThread().getName()+" sale ticket "+this.ticket--);
}
}
}
}
package com.example;
public class Test {
public static void main(String[] args)throws InterruptedException {
MyThread myThread=new MyThread();
Thread t1=new Thread(myThread);
Thread t2=new Thread(myThread);
Thread t3=new Thread(myThread);
t1.start();
t2.start();
t3.start();
}
}
结果
三个线程共享一个runable对象
Thread-1 sale ticket 9
Thread-1 sale ticket 7
Thread-1 sale ticket 6
Thread-1 sale ticket 5
Thread-1 sale ticket 4
Thread-1 sale ticket 3
Thread-2 sale ticket 8
Thread-2 sale ticket 1
Thread-1 sale ticket 2
Thread-0 sale ticket 10
package com.example;
public class MyThread implements Runnable{
private int ticket=10;
@Override
public void run() {
for (int i=0;i<20;i++){
if(this.ticket>0){
try {//这里休眠1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" sale ticket "+this.ticket--);
}
}
}
}
结果
Thread-0 sale ticket 10
Thread-1 sale ticket 9
Thread-2 sale ticket 8
Thread-0 sale ticket 7
Thread-2 sale ticket 6
Thread-1 sale ticket 5
Thread-0 sale ticket 4
Thread-2 sale ticket 3
Thread-1 sale ticket 2
Thread-0 sale ticket 1
Thread-1 sale ticket 0
Thread-2 sale ticket -1
说明这个是线程不同步的,在Thread-2读取值之后,Thread-1将值减到了0,而Thread-2还是会继续运行。
Thread实现多线程
package com.example;
public class MyThread extends Thread{
private int ticket=10;
@Override
public void run() {
for (int i=0;i<20;i++){
if(this.ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" sale ticket "+this.ticket--);
}
}
}
}
package com.example;
public class Test {
public static void main(String[] args)throws InterruptedException {
MyThread t1=new MyThread();
MyThread t2=new MyThread();
MyThread t3=new MyThread();
t1.start();
t2.start();
t3.start();
}
}
Thread中start与run的区别
start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。
run() : run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!
new 一个 Thread,线程进入了新建状态;调用 start() 方法,会启动一个线程并使线程进入了就绪状态,
当分配到时间片后就可以开始运行了。
start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。
- 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,
- 并不会在某个线程中执行它,所以这并不是多线程工作。
- 总结:
- 调用 start 方法方可启动线程并使线程进入就绪状态,
- 而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行
实现多线程方式1:继承Thread类,作为线程对象存在(继承Thread对象)
public class CreatThreadDemo1 extends Thread{
/**
* 构造方法: 继承父类方法的Thread(String name);方法
* @param name
*/
public CreatThreadDemo1(String name){
super(name);
}
@Override
public void run() {
while (!interrupted()){
System.out.println(getName()+"线程执行了...");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
CreatThreadDemo1 d1 = new CreatThreadDemo1("first");
CreatThreadDemo1 d2 = new CreatThreadDemo1("second");
d1.start();
d2.start();
d1.interrupt(); //中断第一个线程
}
}
常规方法,不多做介绍了,interrupted方法,是来判断该线程是否被中断。
终止线程不允许用stop方法,该方法不会施放占用的资源。
所以我们在设计程序的时候,要按照中断线程的思维去设计,就像上面的代码一样。
让线程等待的方法
Thread.sleep(200); //线程休息2ms
Object.wait(); //让线程进入等待,直到调用Object的notify或者notifyAll时,线程停止休眠
实现多线程方式2:实现runnable接口,作为线程任务存在
public class CreatThreadDemo2 implements Runnable {
@Override
public void run() {
while (true){
System.out.println("线程执行了...");
}
}
public static void main(String[] args) {
//将线程任务传给线程对象
Thread thread = new Thread(new CreatThreadDemo2());
//启动线程
thread.start();
}
}
Runnable 只是来修饰线程所执行的任务,它不是一个线程对象。想要启动Runnable对象,必须将它放到一个线程对象里。
实现所线程方式3:匿名内部类创建线程对象
public class CreatThreadDemo3 extends Thread{
public static void main(String[] args) {
//创建无参线程对象
new Thread(){
@Override
public void run() {
System.out.println("线程执行了...");
}
}.start();
//创建带线程任务的线程对象
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行了...");
}
}).start();
//创建带线程任务并且重写run方法的线程对象
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("runnable run 线程执行了...");
}
}){
@Override
public void run() {
System.out.println("override run 线程执行了...");
}
}.start();
}
}
创建带线程任务并且重写run方法的线程对象中,为什么只运行了Thread的run方法。我们看看Thread类的源码
我们可以看到Thread实现了Runnable接口,而Runnable接口里有一个run方法。
所以,我们最终调用的重写的方法应该是Thread类的run方法。而不是Runnable接口的run方法。
实现多线程方式4:创建带返回值的线程
public class CreatThreadDemo4 implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CreatThreadDemo4 demo4 = new CreatThreadDemo4();
FutureTask<Integer> task = new FutureTask<Integer>(demo4); //FutureTask最终实现的是runnable接口
Thread thread = new Thread(task);
thread.start();
System.out.println("我可以在这里做点别的业务逻辑...因为FutureTask是提前完成任务");
//拿出线程执行的返回值
Integer result = task.get();
System.out.println("线程中运算的结果为:"+result);
}
//重写Callable接口的call方法
@Override
public Object call() throws Exception {
int result = 1;
System.out.println("业务逻辑计算中...");
Thread.sleep(3000);
return result;
}
}
Callable接口介绍:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
返回指定泛型的call方法。然后调用FutureTask对象的get方法得道call方法的返回值。
多线程实现方式5:定时器Timer
public class CreatThreadDemo5 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("定时器线程执行了...");
}
},0,1000); //延迟0,周期1s
}
}
实现多线程方式6:线程池创建线程
public class CreatThreadDemo6 {
public static void main(String[] args) {
//创建一个具有10个线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
long threadpoolUseTime = System.currentTimeMillis();
for (int i = 0;i<10;i++){
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行了...");
}
});
}
long threadpoolUseTime1 = System.currentTimeMillis();
System.out.println("多线程用时"+(threadpoolUseTime1-threadpoolUseTime));
//销毁线程池
threadPool.shutdown();
threadpoolUseTime = System.currentTimeMillis();
}
}
实现多线程方式7:利用java8新特性 stream 实现并发
lambda表达式不懂的,可以看看我的java8新特性文章:
java8-lambda:
https://www.jianshu.com/p/3a08dc78a05f
java8-stream:
https://www.jianshu.com/p/ea16d6712a00
public class CreatThreadDemo7 {
public static void main(String[] args) {
List<Integer> values = Arrays.asList(10,20,30,40);
//parallel 平行的,并行的
int result = values.parallelStream().mapToInt(p -> p*2).sum();
System.out.println(result);
//怎么证明它是并发处理呢
values.parallelStream().forEach(p-> System.out.println(p));
}
}
多线程的创建与线程安全
实现多线程的两种方式
继承Thread类,重写Thread类中的run方法
public class MyThread extends Thread{ @Override public void run(){ super.run(); System.out.println("this is myThread run"); } }
public static void main(String[] args) { Thread myThread=new MyThread(); myThread.start(); System.out.println("mian function is over"); }
注意:代码的顺序并不是线程的执行顺序,start的顺序也不是多个线程的执行顺序。
实现Runable接口
java是单继承的,一个子类只能去继承一个父类,所以如果线程类已经继承了其他父类,那么就不能采用继承thread类来实现多线程了
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("this is myrunable running");
}
}
public class Test {
public static void main(String[] args) {
Runnable myRunnable=new MyRunnable();
Thread thread=new Thread(myRunnable);
thread.start();
System.out.println("main is runing");
}
}
实例变量与线程安全:
数据不共享的实例,两个线程中分别创建了两个thread实例,各种都是独立的count
public class MyThread extends Thread{
private int count=5;
public MyThread(String name) {
super();
this.setName(name);
}
@Override
public void run(){
super.run();
while(count>0){
count--;
System.out.println(this.currentThread().getName());
System.out.println(count);
}
}
}
public class Test {
public static void main(String[] args) {
Thread thread1=new MyThread("A");
Thread thread2=new MyThread("B");
thread1.start();
thread2.start();
}
}
数据共享的实例
public class MyThread extends Thread { private int count = 5; @Override public void run() { super.run(); count--; System.out.println(this.currentThread().getName()+count); } } public class Test { public static void main(String[] args) { Thread thread=new MyThread(); Thread thread1=new Thread(thread, "A"); Thread thread2=new Thread(thread,"B"); Thread thread3=new Thread(thread, "C"); thread1.start(); thread2.start(); thread3.start(); } }
可以在方法上加同步锁,syschronized,这样每一个线程在执行这个方法前需要先尝试去获取这把锁,获取不到就会等待,一直到获取到。
public class MyThread extends Thread {
private int count = 5;
@Override
synchronized public void run() {
super.run();
count--;
System.out.println(this.currentThread().getName()+count);
}
}
下面是一个多线程非安全对的共享变量实例:
在实例中共享了静态变量usanameRef和passwordRef
public class Alogin extends Thread{
@Override
public void run(){
LoginServlet.doPost("a", "aa");
}
}
public class Blogin extends Thread{
@Override
public void run(){
LoginServlet.doPost("b", "bb");
}
}
public class LoginServlet {
private static String usenameRef;
private static String passwordRef;
synchronized public static void doPost(String usename,String password){
try {
usenameRef=usename;
if(usename.equals("a")){
Thread.sleep(2000);
}
passwordRef=password;
System.out.println("username="+usenameRef+"password="+password);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
Alogin alogin=new Alogin();
alogin.start();
Blogin blogin=new Blogin();
blogin.start();
}
}
参考文章:
Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式
并发基础篇(六):深入线程Thread类的start()方法和run()方法
Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式
Java多线程系列--“基础篇”03之 Thread中start()和run()的区别
参考: 《java多线程编程核心技术》
https://blog.csdn.net/iaiti/article/details/53314149


浙公网安备 33010602011771号