java多线程中:有三种方式创建线程
1.继承Thread()
public class ThreadClass extends Thread{
private String name;
public ThreadClass(){}
public ThreadClass(String name)
{
this.name=name;
}
public void run()
{
System.out.println(name);
}
public static void main(String str[])
{
ThreadClass tc=new ThreadClass("我是继承Thread创建的线程");
tc.start();
}
}
2.实现Runnable接口创建
public class MyThread implements Runnable{
private String name;
public MyThread(){}
public MyThread(String name)
{
this.name=name;
}
@Override
public void run() {
System.out.println(name);
}
public static void main(String str[])
{
Runnable tc=new MyThread("我是实现Runnable创建的线程");
Thread mt=new Thread(tc);
mt.start();
}
}
优点是Runnable到处可以复用。
3.最省事的创建,也是基于Runnable的
Thread t= new Thread(new Runnable(){
@Override
public void run() {}});
t.start();
2:创建完了,我们来使用。
首先看下常用的几个API:
sleep(); // 调用方式: 线程.sleep(时间); 代表线程休息多少秒后,再加入争抢; 主线程休息的话是:Thread.sleep(1000) 表示主线程休息1000毫秒
join(); //运用场景:比如现在要上传10张图片,这10张图片放在多个子线程中上传,我们必须得让子线程上传完整后在通知父线程做接下来的工作,因此join()就发挥了作用了。直接上例子:
线程1:
public class MyThread implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++)
{
System.out.print(i+" ");
}
}
}
线程2:
public class MyThread1 implements Runnable{
public MyThread1()
{}
@Override
public void run() {
for(int i=0;i<100;i++)
{
System.out.print(i+" ");
}
}
}
测试:
public class TestExample {
public static void main(String args[]) throws InterruptedException
{
Runnable one=new MyThread();
Thread t=new Thread(one);
Runnable two=new MyThread1();
Thread t2=new Thread(two);
t.start();
t2.start();
t.join(); //t执行完才到主线程
t2.join(); //t2执行完猜到主线程
Thread.sleep(5000); //主线程先休息500毫秒先
System.out.print("到我执行了");
}
}
结果:两个子线程执行完后才到主线程。
第二部分线程安全,数据安全的问题:
先来看个不安全的情况:直接上代码:
数据不安全:
public class TestnoThread {
private static int sum=500;
public static void main(String[] args) throws Exception {
Thread t= new Thread(new Runnable(){
@Override
public void run() {
while(sum>0)
{
System.out.print("t1="+sum+" ");
System.out.print("我是T1");
System.out.println();
sum--;
}
}});
Thread t2= new Thread(new Runnable(){
@Override
public void run() {
while(sum>0)
{
System.out.print("t2="+sum+" ");
System.out.print("我是T2");
System.out.println();
sum--;
}
}});
Thread.sleep(1000);
t.start();
t2.start();
}
}
测试的结果:
t1=500 t2=500 我是T2
t2=499 我是T2
t2=498 我是T2
t2=497 我是T2
t2=496 我是T2
t2=495 我是T2
t2=494 我是T2
t2=493 我是T2
t2=492 我是T2
t2=491 我是T2
t2=490 我是T2
500被打印了两次,这就是数据不安全了;
如何解决呢:
让500按顺序来打印: 这时需要引入 3个重要的API
第一: synchronized()
第二: sumtwo.notify(); 通知那些调用了 sumtwo.wait()的线程加入争抢中。
第三: sumtwo.wait(); 中断sumtwo线程,需要sumtwo.notify()的唤醒。
让500按顺序打印,不重复打印,也不缺少,两个线程打印:主要控制sum自减不出现差错就可以了,所以一个信号量可以搞定;下面是例子
public class TestThwo {
private static int sum=500;
private static Object sigh=new Object();
public static void main(String[] args) throws Exception {
Thread t= new Thread(new Runnable(){
@Override
public void run() {
while(sum>0)
{
synchronized(sigh)
{
System.out.print("t1="+sum+" ");
System.out.print("我是T1");
System.out.println();
sum--;
}
}
}});
Thread t2= new Thread(new Runnable(){
@Override
public void run() {
while(sum>0)
{
synchronized(sigh)
{
System.out.print("t2="+sum+" ");
System.out.print("我是T2");
System.out.println();
sum--;
}
}
}});
Thread.sleep(1000);
t.start();
t2.start();
}
}
下面是结果:
t1=500 我是T1
t1=499 我是T1
t1=498 我是T1
t1=497 我是T1
t1=496 我是T1
t1=495 我是T1
t1=494 我是T1
t1=493 我是T1
t1=492 我是T1
t1=491 我是T1
t1=490 我是T1
t1=489 我是T1
t1=488 我是T1
t1=487 我是T1
。。。。。
t1=443 我是T1
t2=442 我是T2
t2=441 我是T2
t2=440 我是T2
t2=439 我是T2
t2=438 我是T2
其实看代码:我们可以发现就是多了一个控制自减所要用到的信号量,和一个控制信号量的东东synchronized(sigh)
private static Object sigh=new Object();
synchronized(sigh)
我们继续改进:让T1和T2轮流从500顺序打印:首先得控制自减,这是一个信号量,其次,得控制顺序,这又是一个信号量,因此总共需要2个信号量:
直接代码献上:
public class TestXhThread {
private static Integer sum=200;
private static Object sums=new Object(); //信号量1
private static Object sumtwo=new Object(); //信号量2
public static void main(String[] args) throws Exception {
Thread t= new Thread(new Runnable(){
@Override
public void run() {
while(sum>0)
{
synchronized(sumtwo) {
synchronized(sums) {
System.out.print("t1="+sum+" ");
System.out.print("我是T1");
System.out.println();
sum--;
sums.notify(); //去通知线程2
}
try {
sumtwo.wait(); //中断线程,等到其他线程使用sumtwo.notify出现的时候,线程被唤醒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}});
Thread t2= new Thread(new Runnable(){
@Override
public void run() {
while(sum>0)
{
synchronized(sums)
{
synchronized(sumtwo) {
System.out.print("t2="+sum+" ");
System.out.print("我是T2");
System.out.println();
sum--;
sumtwo.notify(); //去通知线程1
}
try {
sums.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}});
Thread.sleep(1000);
t.start();
t2.start();
}
}
效果图:
t2=200 我是T2
t1=199 我是T1
t2=198 我是T2
t1=197 我是T1
t2=196 我是T2
t1=195 我是T1
t2=194 我是T2
t1=193 我是T1
t2=192 我是T2
最后附上收藏了一个控制三个变量循环打印的例子:网上看到的,感觉理解了它很多线程问题都可以解决了。
public class ThreadAbc implements Runnable{
private String name;
private Object prev;
private Object selt;
public ThreadAbc(String name, Object prev, Object selt) {
super();
this.name = name;
this.prev = prev;
this.selt = selt;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (selt) {
System.out.print(name);
count--;
selt.notify(); //设防自身所,唤醒下一个等待的进程
}
try {
prev.wait(); //释放前一个锁,终止当前线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
ThreadAbc pa = new ThreadAbc("A", c, a);
ThreadAbc pb = new ThreadAbc("B", a, b);
ThreadAbc pc = new ThreadAbc("C", b, c);
new Thread(pa).start();
Thread.sleep(100); //确保按顺序A、B、C执行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
//线程池与自造线程的资源损耗和效率:
线程池(推荐)
线程池:
public static void main(String[] args) throws IOException, InterruptedException {
long start = System.currentTimeMillis();
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(9);
for ( int i = 0; i <100; i++) {
fixedThreadPool.execute(new suotu(i));
}
fixedThreadPool.shutdown();
while(true)
{
if (fixedThreadPool.isTerminated()) {
System.out.println("结束了!");
break;
}
Thread.sleep(200);
}
System.out.println(System.currentTimeMillis()-start);
}
一个任务一个线程:消耗资源极大(不推荐)
public static void main(String[] args) throws IOException, InterruptedException {
long start = System.currentTimeMillis();
List<Thread>ls=new ArrayList<>();
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(9);
for ( int i = 0; i <200; i++) {
Thread t=new Thread(new suotu(i));
ls.add(t);
}
for(int i=0;i<ls.size();i++)
{
ls.get(i).start();
}
for(int i=0;i<ls.size();i++)
{
ls.get(i).join();
}
System.out.println(System.currentTimeMillis()-start);
}
}